package istar.impl;


import istar.IActor;
import istar.IDependency;
import istar.IElement;
import istar.agent.IstarJadeAgent;

import jade.core.behaviours.Behaviour;

import java.util.Iterator;
import java.util.List;
import java.util.Vector;

public abstract class AbstractElement implements IElement{

	private IActor owner=null;
	private boolean achieved = false;
	private String name, id;
	private List<Object> content = new Vector<Object>();
	private boolean main = true;
	private MeansEndLink meansEndLink; 
	private List<IDependency> dependencies = new Vector<IDependency>();
	private List<TaskDecompositionLink> supports = new Vector<TaskDecompositionLink>();

	private String executionNumber;
	
	@Override
	public Object clone(IActor newOwner) throws CloneNotSupportedException {
		AbstractElement newClone = (AbstractElement)super.clone();
		newClone.content = new Vector<Object>();
		newClone.setSupports(new Vector<TaskDecompositionLink>());
		if (newOwner!=null)
			newClone.setOwner(newOwner);
		newClone.name = this.name;
		newClone.id = this.id;
		newClone.main = this.main;
		if (meansEndLink!=null){
			MeansEndLink newCloneLink = (MeansEndLink)meansEndLink.clone(newOwner);
			try {
				newCloneLink.setOwner(newClone);
			} catch (Exception e) {
				e.printStackTrace();
			}
			newClone.meansEndLink = newCloneLink;
		}
		
		
		Iterator<IDependency> itDeps = dependencies.iterator();
		while (itDeps.hasNext()){
			Dependency dep = (Dependency)itDeps.next();
			//newClone.dependencies.add((Dependency)dep.clone());
			dep.clone();
		}
		return newClone;
	}

	public AbstractElement(String _name){
		this.name = _name;
	}
	
	public IElement getPreviousElement(){
		IElement previous=null;
		
		if (!this.supports.isEmpty()){
			Iterator<TaskDecompositionLink> itTaskDecLinks = this.supports.iterator();
			while (itTaskDecLinks.hasNext()){
				Iterator<IElement> itSubElemnts = itTaskDecLinks.next().getTargetElements().iterator();
				while (itSubElemnts.hasNext()){
					IElement current = itSubElemnts.next(); 
					if (current.equals(this)){
						return previous;
					}
					else {
						previous = current;
					}
				}
			}
		}
		
		return previous;
	}
	
	public AbstractElement(String _name, IActor _owner){
		this.name = _name;
		this.owner = _owner;
		_owner.getInboundaryElements().add(this);
	}
	
	@Override
	public String getName() {
		return this.name;
	}

	@Override
	public void setName(String value) {
		this.name = value;
	}

	@Override
	public IElement getMain(){
		if (this.isMain()){
			return this;
		}
		else {
			if (!this.getSupports().isEmpty()){
				return this.getSupports().get(0).getOwner().getMain();
			}
			
			if (this.getClass().equals(Task.class)){
				return ((Task)this).getMeansFor().get(0).getOwner().getMain();
			}
			try {
				AbstractBasicTask task = (AbstractBasicTask)this;
				return task.getMeansFor().get(0).getOwner().getMain();
			} catch (Exception e) {
				return null;
			}
			
		}
	}
	
	@Override
	public String getExecutionNumber() {
		return executionNumber;
	}

	public void setExecutionNumber(String executionNumber) {
		this.executionNumber = executionNumber;
		if (this.getMeansEndLink()!= null){
			Iterator<IElement> itMeans = this.getMeansEndLink().getTargetElements().iterator();
			while (itMeans.hasNext()){
				AbstractElement mean = (AbstractElement)itMeans.next();
				mean.setExecutionNumber(executionNumber);
			}
		}
	}


	public String getId() {
		return this.id;
	}


	public void setId(String id) {
		this.id = id;
	}

	/*
	@Override
	public Object getContent() {
		return content;
	}
	
	
	@Override
	public void setContent(Object content) {
		this.content = content;
	}
	*/
	
	
	
	@Override
	public List<IDependency> getDependencies(){
		return this.dependencies;
	}

	@Override
	public void setDependencies(List<IDependency> dependencies) {
		this.dependencies = dependencies;
	}
	
	@Override
	public IActor getOwner() {
		return owner;
	}

	@Override
	public void setOwner(IActor owner) {
		this.owner = owner;
		owner.getInboundaryElements().add(this);
		//setSubOwner();
	}
	
	/*
	public void setSubOwner(){
		if (this.meansEndLink!=null){
			Iterator<IElement> itSubs = this.meansEndLink.getTargetElements().iterator();
			while (itSubs.hasNext()){
				IElement subElmnt = itSubs.next();
				if (subElmnt.getOwner()!=null){
					subElmnt.setOwner(this.owner);
					((AbstractElement)subElmnt).setSubOwner();
				}
			}
		}
		
	}
	*/
	
	@Override
	public boolean isMain() {
		//return this.main;
		Iterator<IElement> itOtherElements = this.getOwner().getInboundaryElements().iterator();
		while (itOtherElements.hasNext()){
			IElement other = itOtherElements.next();
			if (other.getMeansEndLink()!=null){
				if (other.getMeansEndLink().getTargetElements().contains(this))
					return false;
			}
			if (other.getClass().equals(Task.class)){
				if (((Task)other).getMyDecompositionLink()!=null){
					if (((Task)other).getMyDecompositionLink().getTargetElements().contains(this))
						return false;
				}
			}
		}
		return true;
	}

	@Override
	public void setMain(boolean value) {
		this.main = value;
		//if (value)
		//	this.getOwner().getMainElements().add(this);
	}
	
	@Override
	public List<TaskDecompositionLink> getSupports() {
		return supports;
	}

	public MeansEndLink getMeansEndLink() {
		return meansEndLink;
	}

	public void setMeansEndLink(MeansEndLink meansEndLink) {
		this.meansEndLink = meansEndLink;
	}

	@Override
	public void setSupports(List<TaskDecompositionLink> supports) {
		this.supports = supports;
	}	
	
	@Override
	public boolean able(IActor Actor) {
		return Actor.able(this);
	}

	@Override
	public boolean isBasic() {
		return false;
	}
	
	@Override
	public boolean workable(IActor Actor) {
		if (able(Actor)){
			if (this.isBasic()){
				return true;
			}
			else{
				if (getMeansEndLink()==null){
					return false;
				}
				else {
					Iterator<IElement> itMeans = meansEndLink.getTargetElements().iterator();
					while (itMeans.hasNext()){
						IElement mean = itMeans.next();
						if (mean.getClass() == Task.class)
							if (((Task)mean).workable(Actor))
								return true;
					}
					return false;
				}
			}
		}
		else
			return false;
	}

	@Override
	public boolean equals(Object arg0) {
		if (arg0.getClass()==this.getClass()){
			if (((AbstractElement)arg0).getName().equals(this.getName()))
				return true;
		}
		return false;
	}

	@Override
	public String getTopic(){
		return (this.getClass().getName()+":"+this.getName());
	}



	
	public boolean isAchieved() {
		return achieved;
	}

	public void setAchieved(boolean achieved) {
		this.achieved = achieved;
	}

	public void Test() throws Exception {

		if (this.getMeansEndLink()!=null) {
			MeansEndLink meansEnd = this.getMeansEndLink();
			Iterator<IElement> itMeans = meansEnd.getTargetElements().iterator();
			while (itMeans.hasNext()){
				IElement mean = itMeans.next();
				
				//System.out.println("	mean id=" + mean.getId() + " name=" + mean.getName());
				
				if (mean.getOwner()!=this.getOwner()){
					throw new Exception("mean has a diferent owner id:"+ mean.getId() + " name=" + mean.getOwner().getName());
				}
			
				if (mean.getClass().equals(Task.class)){
					Iterator<MeansEndLink> itMeansFor = ((Task)mean).getMeansFor().iterator();
					while (itMeansFor.hasNext()){
						IElement end = itMeansFor.next().getOwner();
						if (end==this){
							throw new Exception("mean has a diferent End id:"+ end.getId() + " name=" + end.getName());
						}
						//System.out.println("		End id=" + end.getId() + " name=" + end.getName());
					}
					

				}
					
				try {
					AbstractBasicTask basicTask = (AbstractBasicTask)mean;
					Iterator<MeansEndLink> itMeansFor = ((Task)basicTask).getMeansFor().iterator();
					while (itMeansFor.hasNext()){
						IElement end = itMeansFor.next().getOwner();
						if (end!=this){
							throw new Exception("mean has a diferent End id:"+ end.getId() + " name=" + end.getName());
						}
						System.out.println("		End id=" + end.getId() + " name=" + end.getName());
					}
				
				} catch (ClassCastException ce) {
					
				} catch (Exception e) {
					throw e;
				}
						
			}
		}
		
		if (this.getClass().equals(Task.class)){
			Iterator<IElement> itSubElemnts= ((Task)this).getMyDecompositionLink().getTargetElements().iterator();
			while (itSubElemnts.hasNext()){
				IElement subElement = itSubElemnts.next();
				
				if (subElement.getOwner()!=this.getOwner()){
					throw new Exception("Subelement has a diferent owner id:"+ subElement.getId() + " name=" + subElement.getName());
				}
				
				Iterator<TaskDecompositionLink> itSupports = subElement.getSupports().iterator();
				while (itSupports.hasNext()){
					Task superTask = (Task)itSupports.next().getOwner();
					if (superTask!=this){
						throw new Exception("SubElement has a diferent Supertask id:"+ subElement.getId() + " name=" + subElement.getName());
					}
				}
				
				System.out.println("		SubElement id=" + subElement.getId() + " name=" + subElement.getName());
			}
			
		}

	}

	public List<Object> getContent() {
		return content;
	}

	@Override
	public List<IElement> getDependeeElements() {
		
		Vector<IElement> dependeeElmnts = new Vector<IElement>();
		Iterator<IElement> itDependums = ((IstarJadeAgent)this.getOwner()).getAllDependeeElements().iterator();
		while (itDependums.hasNext()){
			IElement dependum = itDependums.next();
			Iterator<IDependency> itDeps = dependum.getDependencies().iterator();
			while (itDeps.hasNext()){
				IElement dependee = itDeps.next().getDependeeElement();
				if (dependee.equals(this)){
					dependeeElmnts.add(dependum);
				}
			}
		}
		return dependeeElmnts;
	}

	/*
	@Override
	public Behaviour getBehaviour() {
		if (!this.isBasic()){
			
		}
		return null;
	}
	*/
	
	@Override
	protected Object clone() throws CloneNotSupportedException {
		// TODO Auto-generated method stub
		return super.clone();
	}

	@Override
	public void clear(){
		this.getContent().clear();
		if (this.getMeansEndLink()!=null){
			for(int i=0;i<this.getMeansEndLink().getTargetElements().size();i++){
				IElement mean = this.getMeansEndLink().getTargetElements().get(i);
				mean.clear();
			}
		}
		
		
		
	}
	
	
}
